Growth and Activation

join = read.csv("guild-activation.csv")
join

leave = read.csv("guild-leavers.csv")
leave

source = read.csv("guild-joins-by-source.csv")
source

Engagement by last 28 days

text = read.csv("popular-text-channels.csv")
text
voice_channel = read.csv("popular-voice-channels.csv")
voice_channel

Historical Engagement

message = read.csv("guild-message-activity.csv")
message

voice = read.csv("guild-voice-activity.csv")
voice

communicator = read.csv("guild-communicators.csv")
communicator

ETL on Growth and Activation

messing around with date time

library

library(lubridate)

datetime example

I grabbed this example from astrostats.psu. The Berkely Stat Dates page Dates and Times in R was a great reference for the code and values for datetime

Code Value
%d Day of the month (decimal number)
%m Month (decimal number)
%b Month (abbreviated)
%B Month (full name)
%y Year (2 digit)
%Y Year (4 digit)
## read in date/time info in format 'm/d/y h:m:s'
dates <- c("02/27/92", "02/27/92", "01/14/92", "02/28/92", "02/01/92")
times <- c("23:03:20", "22:29:56", "01:03:30", "18:21:03", "16:56:26")
x <- paste(dates, times)
strptime(x, "%m/%d/%y %H:%M:%S")
[1] "1992-02-27 23:03:20 CST" "1992-02-27 22:29:56 CST" "1992-01-14 01:03:30 CST" "1992-02-28 18:21:03 CST" "1992-02-01 16:56:26 CST"
strptime(x, "%m/")
[1] NA NA NA NA NA

tests to investigate how to extract date time

These were scuffed tests I used to learn how to extract the date time * the variable test made me realize removing +00:00 and replacing it with a Z would make the data in a format that can be read by R * the variable test2 was my attempt to try getting it for an entire column

test = "2021-03-27T00:00:00Z"
str(ymd_hms(test))
 POSIXct[1:1], format: "2021-03-27"
test2 = join$interval_start_timestamp
#test2
#ymd_hms(join$interval_start_timestamp)

#strptime(test2, "%Y-%m-%dT%H:%M:%SZ")

While performing my tests, I struggled understanding format of the date was in, a search of a 2021-03-27T00:00:00+00:00 datatype pointed me to a stack overflow page that helped me learn more about python functions Date Time Formats in Python.

testing substring removal

  • with a understanding of what I needed to make it possible, I moved on to learn about substring replacement. This took a long time to figure out and understand.

removing the plus sign

a search of R remove all text after plus sign helped me break through this barrier I found that this answer on stackoverflow was particularly helpful in removing the + sign How to remove + (plus sign) from string in R?. gsub seemed to be the recommend choice among all answers

removing the rest of zeros

I found the following stackoverflow answer that had a example for how to remove the rest of a string Remove all text before colon. I couldn’t remember how to remove everything after the + so the following example from stevencarlislewalker’s blog was particularly helpful in refreshing my memory Remove (or replace) everything before or after a specified character in R strings

gsub("\\+.*", 'Z', "2021-03-27T00:00:00+00:00")
[1] "2021-03-27T00:00:00Z"

removing +00:00Z from the whole column

these were tests I ran to automate this for all the datetime rows.

#join[1,1] = gsub("\\+.*", 'Z', join[1,1])
#join

join[,1] = gsub("\\+.*", 'Z', join[,1])
join
NA

split the interval_start_timestamp

Once I got it working on a row, I applied what I learned above to extract the year, month, and day from the initial datetime object Later when I was generating the bar charts, I had issues ordering the data by calendar months, a quick search yielded Sorting months in R I learned that passing months into factor with the levels = month.name would allow me to sort by the months

year = year(as.POSIXlt(join$interval_start_timestamp))

month = factor(months(as.POSIXlt(join$interval_start_timestamp)),levels = month.name)

day = weekdays(as.POSIXlt(join$interval_start_timestamp))

make the new dataframe

After making the split dataframes, I used a cbind to append the columns to the original dataset and reordered the dataset.

joins = cbind(join, year, month,day)
joins

joins = joins[,c(1,5,6,7,2,3,4)]
joins

testing if I could change the months to become a factor

# test to see what would happen if I could convert a months output as a factor
factor(months(as.POSIXlt(join$interval_start_timestamp)),levels = month.name)[1:20]
 [1] March March March April April April April April April April April April April April April April April April April April
Levels: January February March April May June July August September October November December

Extracting date time

run the following cell to extract year, month, day

joins extraction

# substring replacement
join[,1] = gsub("\\+.*", 'Z', join[,1])

# individual extraction
year = factor(year(as.POSIXlt(join[,1])))
month = factor(months(as.POSIXlt(join[,1])),levels = month.name)
day = weekdays(as.POSIXlt(join[,1]))

# appending new indivually extracted dates
joins = cbind(join, year, month,day)
joins = joins[,c(1,5,6,7,2,3,4)]
joins

sources extraction

# substring replacement
source[,1] = gsub("\\+.*", 'Z', source[,1])

# individual extraction
year = factor(year(as.POSIXlt(source[,1])))
month = factor(months(as.POSIXlt(source[,1])),levels = month.name)
day = weekdays(as.POSIXlt(source[,1]))

# appending new indivually extracted dates
sources = cbind(source, year, month,day)
sources = sources[,c(1,5,6,7,2,3,4)]
sources

leaves extraction

# substring replacement
leave[,1] = gsub("\\+.*", 'Z', leave[,1])

# individual extraction
year = factor(year(as.POSIXlt(leave[,1])))
month = factor(months(as.POSIXlt(leave[,1])),levels = month.name)
day = weekdays(as.POSIXlt(leave[,1]))

# appending new indivually extracted dates
leave
leaves = cbind(leave, year, month,day)
leaves
leaves = leaves[,c(1,4,5,6,2,3)]
leaves

messages extraction

# substring replacement
message[,1] = gsub("\\+.*", 'Z', message[,1])

# individual extraction
year = factor(year(as.POSIXlt(message[,1])))
month = factor(months(as.POSIXlt(message[,1])),levels = month.name)
day = weekdays(as.POSIXlt(message[,1]))

# appending new indivually extracted dates
messages = cbind(message, year, month,day)
messages
messages = messages[,c(1,4,5,6,2,3)]
messages

voices extraction

# substring replacement
voice[,1] = gsub("\\+.*", 'Z', voice[,1])

# individual extraction
year = factor(year(as.POSIXlt(voice[,1])))
month = factor(months(as.POSIXlt(voice[,1])),levels = month.name)
day = weekdays(as.POSIXlt(voice[,1]))

# appending new indivually extracted dates
voices = cbind(voice, year, month,day)
voices = voices[,c(1,3,4,5,2)]
voices

communicators extraction

# substring replacement
communicator[,1] = gsub("\\+.*", 'Z', communicator[,1])

# individual extraction
year = factor(year(as.POSIXlt(communicator[,1])))
month = factor(months(as.POSIXlt(communicator[,1])),levels = month.name)
day = weekdays(as.POSIXlt(communicator[,1]))
communicator

# appending new individually extracted dates
communicators = cbind(communicator, year, month,day)
communicators = communicators[,c(1,4,5,6,2,3)]
communicators$total_communicated = communicators$visitors * communicators$pct_communicated/100

Additional modifications

The following modifications are my attempts to identify covid years for our analysis, I could edit the csv, but I decided to explore R to practice etl for larger datasets. The Fall 2017 STAT 200 course page on Regression With Factor Variables was particularly helpful as a reference when I was trying to have R use Covid as the default factor instead of Normal, having Covid as the default factor will be important when I generate the linear models and interpret the outputs. I would also recommend reading the berkley stats page on “Factors in R” to get a deeper understanding of how to convert factors with dates

I could have applied the relevel() to the as.factor line as seen in this stack overflow answer How to force R to use a specified factor level as reference in a regression?, but I realized it was much easier to read/run the code in my head line by line than to pass into multipe functions

# marking covid and non covid months
joins$year_type = as.double(joins$year)
joins$year_type[joins$year_type == 1 ] <- "Normal"
joins$year_type[joins$year_type == 2] <- "Covid"
joins$year_type[joins$year_type == 3] <- "Covid"
joins$year_type = as.factor(joins$year_type)
joins$year_type = relevel(joins$year_type, ref = 2)
joins

leaves$year_type = as.double(leaves$year)
leaves$year_type[leaves$year_type == 1 ] <- "Normal"
leaves$year_type[leaves$year_type ==2] <- "Covid"
leaves$year_type[leaves$year_type ==3] <- "Covid"
leaves$year_type = as.factor(leaves$year_type)
leaves$year_type = relevel(leaves$year_type, ref = 2)
leaves

sources$year_type = as.double(sources$year)
sources$year_type[sources$year_type == 1 ] <- "Normal"
sources$year_type[sources$year_type ==2] <- "Covid"
sources$year_type[sources$year_type ==3] <- "Covid"
sources$year_type = as.factor(sources$year_type)
sources$year_type = relevel(sources$year_type, ref = 2)
sources

messages$year_type = as.double(messages$year)
messages$year_type[messages$year_type == 1 ] <- "Normal"
messages$year_type[messages$year_type ==2] <- "Covid"
messages$year_type[messages$year_type ==3] <- "Covid"
messages$year_type = as.factor(messages$year_type)
messages$year_type = relevel(messages$year_type, ref = 2)
messages


voices$year_type = as.double(voices$year)
voices$year_type[voices$year_type == 1 ] <- "Normal"
voices$year_type[voices$year_type ==2] <- "Covid"
voices$year_type[voices$year_type ==3] <- "Covid"
voices$year_type = as.factor(voices$year_type)
voices$year_type = relevel(voices$year_type, ref = 2)
voices

communicators$year_type = as.double(communicators$year)
communicators$year_type[communicators$year_type == 1 ] <- "Normal"
communicators$year_type[communicators$year_type ==2] <- "Covid"
communicators$year_type[communicators$year_type ==3] <- "Covid"
communicators$year_type = as.factor(communicators$year_type)
communicators$year_type = relevel(communicators$year_type, ref = 2)
communicators

data needed for investigation

historical data

joins
leaves
sources
messages
voices
communicators

last 28 days

text
voice

subsetting by year

Originally I planned on aggregating by the year for my bar charts, but when I read through some more examples of aggregates, I found a better method in “Aggregating by category”

joins.2019 = subset(joins, year == 2019)
joins.2020 = subset(joins, year == 2020)
joins.2021 = subset(joins, year == 2021)

leaves.2019 = subset(leaves, year == 2019)
leaves.2020 = subset(leaves, year == 2020)
leaves.2021 = subset(leaves, year == 2021)

sources.2019 = subset(sources, year == 2019)
sources.2020 = subset(sources, year == 2020)
sources.2021 = subset(sources, year == 2021)

comm.2019 = subset(communicators, year == 2019)
comm.2020 = subset(communicators, year == 2020)
comm.2021 = subset(communicators, year == 2021)

Aggregating by year

2019

joins.2019
leaves.2019
sources.2019
comm.2019

2020

joins.2020
leaves.2020
sources.2020
comm.2020

2021

joins.2021
leaves.2021
sources.2021
comm.2021

Aggregating by month

2019

joins.2019
leaves.2019
comm.2019

agg_joins.2019 = aggregate(joins.2019$new_members, list(joins.2019$month), sum)
colnames(agg_joins.2019) <- c("Months", "Total New Members")
agg_leaves.2019 = aggregate(leaves.2019$leavers, list(leaves.2019$month), sum)
colnames(agg_leaves.2019) <- c("Months", "Total Leavers")
agg_comm.2019 = aggregate(comm.2019$total_communicated, list(comm.2019$month), sum)
colnames(agg_comm.2019) <- c("Months", "Total Communicated")

agg_joins.2019[order(med_joins.2019$x),]
agg_leaves.2019[order(med_leaves.2019$x),]
agg_comm.2019[order(med_comm.2019$x),]

2020

joins.2020
leaves.2020
comm.2020

agg_joins.2020 = aggregate(joins.2020$new_members, list(joins.2020$month), sum)
colnames(agg_joins.2020) <- c("Months", "Total New Members")
agg_leaves.2020 = aggregate(leaves.2020$leavers, list(leaves.2020$month), sum)
colnames(agg_leaves.2020) <- c("Months", "Total Leavers")
agg_comm.2020 = aggregate(comm.2020$total_communicated, list(comm.2020$month), sum)
colnames(agg_comm.2020) <- c("Months", "Total Communicated")


agg_joins.2020[order(med_joins.2020$x),]
agg_leaves.2020[order(med_leaves.2020$x),]
agg_comm.2020[order(med_comm.2020$x),]

2021

joins.2021
leaves.2021
comm.2021

agg_joins.2021 = aggregate(joins.2021$new_members, list(joins.2021$month), sum)
colnames(agg_joins.2021) <- c("Months", "Total New Members")
agg_leaves.2021 = aggregate(leaves.2021$leavers, list(leaves.2021$month), sum)
colnames(agg_leaves.2021) <- c("Months", "Total Leavers")
agg_comm.2021 = aggregate(comm.2021$total_communicated, list(comm.2021$month), sum)
colnames(agg_comm.2021) <- c("Months", "Total Communicated")



agg_joins.2021[order(med_joins.2021$x),]
agg_leaves.2021[order(med_leaves.2021$x),]
agg_comm.2021[order(med_comm.2021$x),]

testing aggregations

communicators
median_comm = aggregate(communicators$visitors, list(communicators$month), sum)
median_comm[order(median_comm$x),]

Aggregating by category

As mentioned in the subsetting by year section, upon reading some examples for aggregating in R, I found that there was a method to aggregate by multiple columns. The following article “Aggregate in R” was particularly helpful as it had sample code with useful outputs. The second option of using R linear model notation is a bit more intuitive than the first suggestion.

aggregate(df_2$weight, by = list(df_2$feed, df_2$cat_var), FUN = sum)

# Equivalent to:
aggregate(weight ~ feed + cat_var, data = df_2, FUN = sum)

joins

joins
agg_joins = aggregate(new_members ~ month + year, data = joins, FUN = sum)
agg_joins

leaves

leaves
agg_leaves = aggregate(leavers ~ month + year, data = leaves, FUN = sum)
agg_leaves

experimental 3d agg

leaves
agg_leaves = aggregate(leavers ~ month + year, data = leaves, FUN = sum)
agg_leaves

sources

looks really weird ignoring for now

sources
agg_sources = aggregate(discovery_joins + invites + vanity_joins ~ month + year, data = sources, FUN = sum)
agg_sources

comms

communicators
agg_comms = aggregate(total_communicated ~ month + year, data = communicators, FUN = sum)
agg_comms

Visualizations

I realized that using R’s base plots were not going to make the cut. I recall that when I was searching for graphing solutions on a different project, I found an appealing graph solution with ggplots. At the time I was using python, so ggplot wasn’t a library supported. In another class, the professor introduced ggplots. I could have used excel to generate the plots, but I wanted a learning opportunity to try ggplot on something that wasn’t homework or classwork. I knew I needed a stacked bar chart as I needed to compare the changes across the months and years.

After a search on the web, I found the following guide “How to Create and Customize Bar Plot Using ggplot2 Package in R- One Zero Blog” on the towards data science medium to be particularly helpful, as there was sample code with outputs. I used the sample code from section on bar labels on a stack bar plot as a base and made modifications to fit my data.

all joins

To make it easier for me to input the parameters, I loaded all the aggregate data, since I wasn’t sure how the graphs would look.

library(ggplot2)

joins
agg_joins.2019
agg_joins.2020
agg_joins.2021
agg_joins

I started by substituting the sample parameters with my own dataset. I quickly realized that the graph had some issues on the x axis. The month names were overlapping.

all_joins = ggplot(data = agg_joins, mapping = aes(x = month, y = new_members, fill = year)) + xlab("Month") + ylab("Total New Members") + geom_col()+ 
            geom_text(aes(label=new_members), position = position_stack(vjust= 0.5),
            colour = "white", size = 5)
all_joins = all_joins + labs(title = "New Member Joins Across the Year")
all_joins

After searching the web, I found a great stack overflow answer How to maintain size of ggplot with long labels that ultimately influenced the final graphs.

all_joins = ggplot(data = agg_joins, mapping = aes(x = month, y = new_members, fill = year)) + xlab("Month") + ylab("Total New Members") + geom_col()+ 
            geom_text(aes(label=new_members), position = position_stack(vjust= 0.5),
            colour = "white", size = 5) + coord_flip()
all_joins = all_joins + labs(title = "New Member Joins Across the Year")
all_joins

When I first made the graphs, the order of the x axis was backwards from a normal year. For the presentation I used the version above, but when I came back for the final report and final write up, I decided to search for a solution. I knew previously that coord_flip() was the cause of the initial reversed order. Searching ggplot coord_flip() change order of x axis found the answer I was looking for. The following answer from Reversed order after coord_flip in R was had the solution I was looking for. I learned that I could use a limits parameter to change the order, as passing scale_x_discrete() with out any parameters wouldn’t change my graph.

Ultimately this is the final version of the graph. For the report, I scaled the horizontal dimension to be 1920 and had the aspect ratio fixed.

all_joins = ggplot(data = agg_joins, mapping = aes(x = month, y = new_members, fill = year)) + xlab("Month") + ylab("Total New Members") + geom_col()+ 
            geom_text(aes(label=new_members), position = position_stack(vjust= 0.5),
            colour = "white", size = 5) + coord_flip() + scale_x_discrete(limits = rev(levels(agg_joins$month)))
all_joins = all_joins + labs(title = "New Member Joins Across the Year")
all_joins

all leaves

I decided to also make a graph for leaves, but it was ultimately scrapped because our analysis was more focused in the new user changes. Perhaps we can return to analyze the leaves

leaves
agg_leaves.2019
agg_leaves.2020
agg_leaves.2021
agg_leaves
all_leaves = ggplot(data = agg_leaves, mapping = aes(x = month, y = leavers, fill = year)) + xlab("Month") + ylab("Total Leaves") + geom_col()+ 
             geom_text(aes(label=leavers), position = position_stack(vjust= 0.5),
             colour = "white", size = 5) + coord_flip() + scale_x_discrete(limits = rev(levels(agg_leaves$month)))
all_leaves = all_leaves + labs(title = "Member Leaves Across the Year")

all_leaves

all communicators

communicators

agg_comm.2019
agg_comm.2020
agg_comm.2021
agg_comms
all_comms = ggplot(data = agg_comms, mapping = aes(x = month, y = total_communicated, fill = year)) + xlab("Month") + ylab("Total Members Communicated") + 
            geom_col()+ geom_text(aes(label=total_communicated), position = position_stack(vjust= 0.5),
            colour = "white", size = 5) + coord_flip() + scale_x_discrete(limits = rev(levels(agg_comms$month)))
all_comms = all_comms + labs(title = "All Communicating Members")
all_comms

linear models

This section contains the code for generating linear models for the other variables we were interested in. I followed my professor’s notes for setting up the parameters. For fun I decided to experiment with the messages dataset, as it included an additional variable of messages_per_communicator which gives a bit more granularity in comparing between individuals and aggregates for messages.

new members linear model

joins
joins_lm = lm(new_members ~ month + year_type, data = joins)
print(summary(joins_lm))

Call:
lm(formula = new_members ~ month + year_type, data = joins)

Residuals:
   Min     1Q Median     3Q    Max 
-8.759 -2.195 -0.612  0.808 85.469 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)     1.98132    0.80555   2.460  0.01414 *  
monthFebruary   0.26401    0.96935   0.272  0.78543    
monthMarch     -0.01493    0.95690  -0.016  0.98756    
monthApril      0.60450    0.98228   0.615  0.53848    
monthMay       -0.36969    0.97461  -0.379  0.70456    
monthJune      -0.46217    0.98228  -0.471  0.63814    
monthJuly      -0.30518    0.97461  -0.313  0.75428    
monthAugust     6.54966    0.97461   6.720 3.70e-11 ***
monthSeptember  4.28783    0.98228   4.365 1.46e-05 ***
monthOctober    2.22708    0.97461   2.285  0.02260 *  
monthNovember   2.25450    0.98228   2.295  0.02201 *  
monthDecember  -0.78905    0.97461  -0.810  0.41844    
year_typeCovid  1.22836    0.44590   2.755  0.00602 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.283 on 716 degrees of freedom
Multiple R-squared:  0.1475,    Adjusted R-squared:  0.1332 
F-statistic: 10.32 on 12 and 716 DF,  p-value: < 2.2e-16

total messages linear model

messages
messages_lm = lm(messages ~ month + year_type, data = messages)
print(summary(messages_lm))

Call:
lm(formula = messages ~ month + year_type, data = messages)

Residuals:
    Min      1Q  Median      3Q     Max 
-533.72 -131.98  -34.98   68.19 2435.80 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)     370.7838    37.3808   9.919  < 2e-16 ***
monthFebruary     0.7405    44.9820   0.016  0.98687    
monthMarch       19.0476    44.4043   0.429  0.66808    
monthApril      153.6371    45.5819   3.371  0.00079 ***
monthMay         24.6162    45.2261   0.544  0.58641    
monthJune       -73.9795    45.5819  -1.623  0.10503    
monthJuly       -42.4322    45.2261  -0.938  0.34845    
monthAugust     210.2452    45.2261   4.649 3.98e-06 ***
monthSeptember  433.9371    45.5819   9.520  < 2e-16 ***
monthOctober    261.9549    45.2261   5.792 1.04e-08 ***
monthNovember   109.9371    45.5819   2.412  0.01612 *  
monthDecember   -79.8354    45.2261  -1.765  0.07795 .  
year_typeCovid -193.5419    20.6915  -9.354  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 245.1 on 716 degrees of freedom
Multiple R-squared:  0.369, Adjusted R-squared:  0.3584 
F-statistic: 34.89 on 12 and 716 DF,  p-value: < 2.2e-16

messages experiments

including messages_per_communicator in full model

messages
messages_lm1 = lm(messages ~ month + year_type + messages_per_communicator, data = messages)
print(summary(messages_lm1))

Call:
lm(formula = messages ~ month + year_type + messages_per_communicator, 
    data = messages)

Residuals:
    Min      1Q  Median      3Q     Max 
-794.57  -58.66    1.20   50.09 1112.68 

Coefficients:
                          Estimate Std. Error t value Pr(>|t|)    
(Intercept)                -80.219     22.265  -3.603 0.000337 ***
monthFebruary               44.936     23.694   1.896 0.058298 .  
monthMarch                  13.590     23.369   0.582 0.561041    
monthApril                  12.429     24.209   0.513 0.607821    
monthMay                   -37.842     23.845  -1.587 0.112952    
monthJune                   -2.577     24.045  -0.107 0.914678    
monthJuly                  -33.459     23.802  -1.406 0.160241    
monthAugust                128.790     23.875   5.394 9.36e-08 ***
monthSeptember             311.849     24.154  12.911  < 2e-16 ***
monthOctober               187.593     23.863   7.861 1.40e-14 ***
monthNovember              101.338     23.989   4.224 2.71e-05 ***
monthDecember              -12.940     23.851  -0.543 0.587613    
year_typeCovid             -36.598     11.478  -3.189 0.001492 ** 
messages_per_communicator   55.895      1.292  43.247  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 129 on 715 degrees of freedom
Multiple R-squared:  0.8255,    Adjusted R-squared:  0.8223 
F-statistic: 260.2 on 13 and 715 DF,  p-value: < 2.2e-16

including messages_per_communicator in full model

messages
messages_lm2 = lm(messages_per_communicator ~ month + year_type, data = messages)
print(summary(messages_lm2))

Call:
lm(formula = messages_per_communicator ~ month + year_type, data = messages)

Residuals:
    Min      1Q  Median      3Q     Max 
-7.5431 -2.2972 -0.7784  1.2309 28.5756 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)     8.06881    0.56882  14.185  < 2e-16 ***
monthFebruary  -0.79070    0.68449  -1.155  0.24841    
monthMarch      0.09763    0.67570   0.144  0.88515    
monthApril      2.52633    0.69362   3.642  0.00029 ***
monthMay        1.11743    0.68821   1.624  0.10489    
monthJune      -1.27745    0.69362  -1.842  0.06593 .  
monthJuly      -0.16054    0.68821  -0.233  0.81561    
monthAugust     1.45731    0.68821   2.118  0.03456 *  
monthSeptember  2.18426    0.69362   3.149  0.00171 ** 
monthOctober    1.33040    0.68821   1.933  0.05361 .  
monthNovember   0.15385    0.69362   0.222  0.82452    
monthDecember  -1.19681    0.68821  -1.739  0.08246 .  
year_typeCovid -2.80785    0.31486  -8.918  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.73 on 716 degrees of freedom
Multiple R-squared:  0.2164,    Adjusted R-squared:  0.2033 
F-statistic: 16.48 on 12 and 716 DF,  p-value: < 2.2e-16

voices linear model

voices
voices_lm = lm(speaking_minutes ~ month + year_type, data = voices)
print(summary(voices_lm))

Call:
lm(formula = speaking_minutes ~ month + year_type, data = voices)

Residuals:
    Min      1Q  Median      3Q     Max 
-928.94 -287.96  -21.33  150.04 2268.59 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)      238.42      68.62   3.475 0.000542 ***
monthFebruary     53.85      82.57   0.652 0.514493    
monthMarch       261.27      81.51   3.205 0.001409 ** 
monthApril      -217.09      83.67  -2.595 0.009665 ** 
monthMay        -269.06      83.02  -3.241 0.001246 ** 
monthJune       -225.25      83.67  -2.692 0.007265 ** 
monthJuly       -265.07      83.02  -3.193 0.001470 ** 
monthAugust      142.77      83.02   1.720 0.085914 .  
monthSeptember   474.25      83.67   5.668 2.09e-08 ***
monthOctober     463.99      83.02   5.589 3.25e-08 ***
monthNovember    256.21      83.67   3.062 0.002280 ** 
monthDecember     -9.41      83.02  -0.113 0.909785    
year_typeCovid   216.28      37.98   5.694 1.81e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 450 on 716 degrees of freedom
Multiple R-squared:  0.2877,    Adjusted R-squared:  0.2757 
F-statistic:  24.1 on 12 and 716 DF,  p-value: < 2.2e-16

communicators linear model

communicators
communicators_lm = lm(total_communicated ~ month + year_type, data = communicators)
print(summary(communicators_lm))

Call:
lm(formula = total_communicated ~ month + year_type, data = communicators)

Residuals:
    Min      1Q  Median      3Q     Max 
-39.805  -7.258  -1.258   5.628  77.195 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)     42.1266     1.9689  21.396  < 2e-16 ***
monthFebruary    4.9875     2.3693   2.105  0.03563 *  
monthMarch       2.7318     2.3388   1.168  0.24318    
monthApril       7.5910     2.4009   3.162  0.00163 ** 
monthMay        -0.3536     2.3821  -0.148  0.88203    
monthJune       -0.4757     2.4009  -0.198  0.84300    
monthJuly       -1.8698     2.3821  -0.785  0.43277    
monthAugust     20.6786     2.3821   8.681  < 2e-16 ***
monthSeptember  41.6910     2.4009  17.365  < 2e-16 ***
monthOctober    23.1141     2.3821   9.703  < 2e-16 ***
monthNovember   12.7410     2.4009   5.307 1.49e-07 ***
monthDecember   -4.6601     2.3821  -1.956  0.05082 .  
year_typeCovid  -8.8685     1.0899  -8.137 1.79e-15 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 12.91 on 716 degrees of freedom
Multiple R-squared:  0.552, Adjusted R-squared:  0.5445 
F-statistic: 73.53 on 12 and 716 DF,  p-value: < 2.2e-16

messing with top values

# dataframe_name[with(dataframe_name, order(column_name)), ]
df=voice[with(voice,order("communicators")),]
df
LS0tCnRpdGxlOiAiSWxsaW5pIEVzcG9ydHMgRW5nYWdlbWVudCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIEdyb3d0aCBhbmQgQWN0aXZhdGlvbgpgYGB7Un0Kam9pbiA9IHJlYWQuY3N2KCJndWlsZC1hY3RpdmF0aW9uLmNzdiIpCmpvaW4KCmxlYXZlID0gcmVhZC5jc3YoImd1aWxkLWxlYXZlcnMuY3N2IikKbGVhdmUKCnNvdXJjZSA9IHJlYWQuY3N2KCJndWlsZC1qb2lucy1ieS1zb3VyY2UuY3N2IikKc291cmNlCmBgYAojIEVuZ2FnZW1lbnQgYnkgbGFzdCAyOCBkYXlzCmBgYHtyfQp0ZXh0ID0gcmVhZC5jc3YoInBvcHVsYXItdGV4dC1jaGFubmVscy5jc3YiKQp0ZXh0CnZvaWNlX2NoYW5uZWwgPSByZWFkLmNzdigicG9wdWxhci12b2ljZS1jaGFubmVscy5jc3YiKQp2b2ljZV9jaGFubmVsCmBgYAojIEhpc3RvcmljYWwgRW5nYWdlbWVudApgYGB7cn0KbWVzc2FnZSA9IHJlYWQuY3N2KCJndWlsZC1tZXNzYWdlLWFjdGl2aXR5LmNzdiIpCm1lc3NhZ2UKCnZvaWNlID0gcmVhZC5jc3YoImd1aWxkLXZvaWNlLWFjdGl2aXR5LmNzdiIpCnZvaWNlCgpjb21tdW5pY2F0b3IgPSByZWFkLmNzdigiZ3VpbGQtY29tbXVuaWNhdG9ycy5jc3YiKQpjb21tdW5pY2F0b3IKYGBgCgojIEVUTCBvbiBHcm93dGggYW5kIEFjdGl2YXRpb24KIyMgbWVzc2luZyBhcm91bmQgd2l0aCBkYXRlIHRpbWUKIyMjIGxpYnJhcnkKYGBge3J9CmxpYnJhcnkobHVicmlkYXRlKQpgYGAKIyMjIGRhdGV0aW1lIGV4YW1wbGUKSSBncmFiYmVkIHRoaXMgZXhhbXBsZSBmcm9tIFthc3Ryb3N0YXRzLnBzdV0oaHR0cHM6Ly9hc3Ryb3N0YXRpc3RpY3MucHN1LmVkdS9zdTA3L1IvaHRtbC9iYXNlL2h0bWwvZm9ybWF0LkRhdGUuaHRtbCkuIFRoZSBCZXJrZWx5IFN0YXQgRGF0ZXMgcGFnZSBbRGF0ZXMgYW5kIFRpbWVzIGluIFJdKGh0dHBzOi8vd3d3LnN0YXQuYmVya2VsZXkuZWR1L35zMTMzL2RhdGVzLmh0bWwpIHdhcyBhIGdyZWF0IHJlZmVyZW5jZSBmb3IgdGhlIGNvZGUgYW5kIHZhbHVlcyBmb3IgZGF0ZXRpbWUgCgp8IENvZGUgfCBWYWx1ZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8ICVkICAgfCBEYXkgb2YgdGhlIG1vbnRoIChkZWNpbWFsIG51bWJlcikgfAp8ICVtICAgfCBNb250aCAoZGVjaW1hbCBudW1iZXIpICAgICAgICAgICAgfAp8ICViICAgfCBNb250aCAoYWJicmV2aWF0ZWQpICAgICAgICAgICAgICAgfAp8ICVCICAgfCBNb250aCAoZnVsbCBuYW1lKSAgICAgICAgICAgICAgICAgfAp8ICV5ICAgfCBZZWFyICgyIGRpZ2l0KSAgICAgICAgICAgICAgICAgICAgfAp8ICVZICAgfCBZZWFyICg0IGRpZ2l0KSAgICAgICAgICAgICAgICAgICAgfAoKYGBge1J9CiMjIHJlYWQgaW4gZGF0ZS90aW1lIGluZm8gaW4gZm9ybWF0ICdtL2QveSBoOm06cycKZGF0ZXMgPC0gYygiMDIvMjcvOTIiLCAiMDIvMjcvOTIiLCAiMDEvMTQvOTIiLCAiMDIvMjgvOTIiLCAiMDIvMDEvOTIiKQp0aW1lcyA8LSBjKCIyMzowMzoyMCIsICIyMjoyOTo1NiIsICIwMTowMzozMCIsICIxODoyMTowMyIsICIxNjo1NjoyNiIpCnggPC0gcGFzdGUoZGF0ZXMsIHRpbWVzKQpzdHJwdGltZSh4LCAiJW0vJWQvJXkgJUg6JU06JVMiKQpzdHJwdGltZSh4LCAiJW0vIikKCmBgYAojIyMgdGVzdHMgdG8gaW52ZXN0aWdhdGUgaG93IHRvIGV4dHJhY3QgZGF0ZSB0aW1lClRoZXNlIHdlcmUgc2N1ZmZlZCB0ZXN0cyBJIHVzZWQgdG8gbGVhcm4gaG93IHRvIGV4dHJhY3QgdGhlIGRhdGUgdGltZQoqIHRoZSB2YXJpYWJsZSBgdGVzdGAgbWFkZSBtZSByZWFsaXplIHJlbW92aW5nIGArMDA6MDBgIGFuZCByZXBsYWNpbmcgaXQgd2l0aCBhIGBaYCB3b3VsZCBtYWtlIHRoZSBkYXRhIGluIGEgZm9ybWF0IHRoYXQgY2FuIGJlIHJlYWQgYnkgUgoqIHRoZSB2YXJpYWJsZSBgdGVzdDJgIHdhcyBteSBhdHRlbXB0IHRvIHRyeSBnZXR0aW5nIGl0IGZvciBhbiBlbnRpcmUgY29sdW1uCgpgYGB7cn0KdGVzdCA9ICIyMDIxLTAzLTI3VDAwOjAwOjAwWiIKc3RyKHltZF9obXModGVzdCkpCgp0ZXN0MiA9IGpvaW4kaW50ZXJ2YWxfc3RhcnRfdGltZXN0YW1wCiN0ZXN0MgojeW1kX2htcyhqb2luJGludGVydmFsX3N0YXJ0X3RpbWVzdGFtcCkKCiNzdHJwdGltZSh0ZXN0MiwgIiVZLSVtLSVkVCVIOiVNOiVTWiIpCmBgYApXaGlsZSBwZXJmb3JtaW5nIG15IHRlc3RzLCBJIHN0cnVnZ2xlZCB1bmRlcnN0YW5kaW5nIGZvcm1hdCBvZiB0aGUgZGF0ZSB3YXMgaW4sIGEgc2VhcmNoIG9mIGEgWzIwMjEtMDMtMjdUMDA6MDA6MDArMDA6MDAgZGF0YXR5cGVdKGh0dHBzOi8vZHVja2R1Y2tnby5jb20vP3E9MjAyMS0wMy0yN1QwMCUzQTAwJTNBMDAlMkIwMCUzQTAwK2RhdGF0eXBlJnQ9ZmZhYiZpYT13ZWIpIHBvaW50ZWQgbWUgdG8gYSBzdGFjayBvdmVyZmxvdyBwYWdlIHRoYXQgaGVscGVkIG1lIGxlYXJuIG1vcmUgYWJvdXQgcHl0aG9uIGZ1bmN0aW9ucyBbRGF0ZSBUaW1lIEZvcm1hdHMgaW4gUHl0aG9uXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xNzU5NDI5OC9kYXRlLXRpbWUtZm9ybWF0cy1pbi1weXRob24pLgoKIyMjIHRlc3Rpbmcgc3Vic3RyaW5nIHJlbW92YWwKKiB3aXRoIGEgdW5kZXJzdGFuZGluZyBvZiB3aGF0IEkgbmVlZGVkIHRvIG1ha2UgaXQgcG9zc2libGUsIEkgbW92ZWQgb24gdG8gbGVhcm4gYWJvdXQgc3Vic3RyaW5nIHJlcGxhY2VtZW50LiBUaGlzIHRvb2sgYSBsb25nIHRpbWUgdG8gZmlndXJlIG91dCBhbmQgdW5kZXJzdGFuZC4KCiMjIyMgcmVtb3ZpbmcgdGhlIHBsdXMgc2lnbgphIHNlYXJjaCBvZiBbUiByZW1vdmUgYWxsIHRleHQgYWZ0ZXIgcGx1cyBzaWduXShodHRwczovL2R1Y2tkdWNrZ28uY29tLz9xPVIrcmVtb3ZlK2FsbCt0ZXh0K2FmdGVyK3BsdXMrc2lnbiZ0PWZmYWImaWE9d2ViKSBoZWxwZWQgbWUgYnJlYWsgdGhyb3VnaCB0aGlzIGJhcnJpZXIgSSBmb3VuZCB0aGF0IHRoaXMgYW5zd2VyIG9uIHN0YWNrb3ZlcmZsb3cgd2FzIHBhcnRpY3VsYXJseSBoZWxwZnVsIGluIHJlbW92aW5nIHRoZSBgK2Agc2lnbiBbSG93IHRvIHJlbW92ZSArIChwbHVzIHNpZ24pIGZyb20gc3RyaW5nIGluIFI/XShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvMzU4MDc3MzcpLiBnc3ViIHNlZW1lZCB0byBiZSB0aGUgcmVjb21tZW5kIGNob2ljZSBhbW9uZyBhbGwgYW5zd2VycwoKIyMjIyByZW1vdmluZyB0aGUgcmVzdCBvZiB6ZXJvcwpJIGZvdW5kIHRoZSBmb2xsb3dpbmcgc3RhY2tvdmVyZmxvdyBhbnN3ZXIgdGhhdCBoYWQgYSBleGFtcGxlIGZvciBob3cgdG8gcmVtb3ZlIHRoZSByZXN0IG9mIGEgc3RyaW5nIFtSZW1vdmUgYWxsIHRleHQgYmVmb3JlIGNvbG9uXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvMTIyOTc5OTEpLiBJIGNvdWxkbid0IHJlbWVtYmVyIGhvdyB0byByZW1vdmUgZXZlcnl0aGluZyBhZnRlciB0aGUgKyBzbyB0aGUgZm9sbG93aW5nIGV4YW1wbGUgZnJvbSBzdGV2ZW5jYXJsaXNsZXdhbGtlcidzIGJsb2cgd2FzIHBhcnRpY3VsYXJseSBoZWxwZnVsIGluIHJlZnJlc2hpbmcgbXkgbWVtb3J5IFtSZW1vdmUgKG9yIHJlcGxhY2UpIGV2ZXJ5dGhpbmcgYmVmb3JlIG9yIGFmdGVyIGEgc3BlY2lmaWVkIGNoYXJhY3RlciBpbiBSIHN0cmluZ3NdKGh0dHBzOi8vc3RldmVuY2FybGlzbGV3YWxrZXIud29yZHByZXNzLmNvbS8yMDEzLzAyLzEzL3JlbW92ZS1vci1yZXBsYWNlLWV2ZXJ5dGhpbmctYmVmb3JlLW9yLWFmdGVyLWEtc3BlY2lmaWVkLWNoYXJhY3Rlci1pbi1yLXN0cmluZ3MvKQoKYGBge3J9CmdzdWIoIlxcKy4qIiwgJ1onLCAiMjAyMS0wMy0yN1QwMDowMDowMCswMDowMCIpCmBgYAojIyByZW1vdmluZyArMDA6MDBaIGZyb20gdGhlIHdob2xlIGNvbHVtbgp0aGVzZSB3ZXJlIHRlc3RzIEkgcmFuIHRvIGF1dG9tYXRlIHRoaXMgZm9yIGFsbCB0aGUgZGF0ZXRpbWUgcm93cy4KYGBge3J9CiNqb2luWzEsMV0gPSBnc3ViKCJcXCsuKiIsICdaJywgam9pblsxLDFdKQojam9pbgoKam9pblssMV0gPSBnc3ViKCJcXCsuKiIsICdaJywgam9pblssMV0pCmpvaW4KCmBgYAojIyMgc3BsaXQgdGhlIGBpbnRlcnZhbF9zdGFydF90aW1lc3RhbXBgCk9uY2UgSSBnb3QgaXQgd29ya2luZyBvbiBhIHJvdywgSSBhcHBsaWVkIHdoYXQgSSBsZWFybmVkIGFib3ZlIHRvIGV4dHJhY3QgdGhlIHllYXIsIG1vbnRoLCBhbmQgZGF5IGZyb20gdGhlIGluaXRpYWwgZGF0ZXRpbWUgb2JqZWN0CkxhdGVyIHdoZW4gSSB3YXMgZ2VuZXJhdGluZyB0aGUgYmFyIGNoYXJ0cywgSSBoYWQgaXNzdWVzIG9yZGVyaW5nIHRoZSBkYXRhIGJ5IGNhbGVuZGFyIG1vbnRocywgYSBxdWljayBzZWFyY2ggeWllbGRlZCBbU29ydGluZyBtb250aHMgaW4gUl0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9hLzk3Njk3MzUpIEkgbGVhcm5lZCB0aGF0IHBhc3NpbmcgYG1vbnRoc2AgaW50byBgZmFjdG9yYCB3aXRoIHRoZSBgbGV2ZWxzID0gbW9udGgubmFtZWAgd291bGQgYWxsb3cgbWUgdG8gc29ydCBieSB0aGUgbW9udGhzCmBgYHtyfQp5ZWFyID0geWVhcihhcy5QT1NJWGx0KGpvaW4kaW50ZXJ2YWxfc3RhcnRfdGltZXN0YW1wKSkKCm1vbnRoID0gZmFjdG9yKG1vbnRocyhhcy5QT1NJWGx0KGpvaW4kaW50ZXJ2YWxfc3RhcnRfdGltZXN0YW1wKSksbGV2ZWxzID0gbW9udGgubmFtZSkKCmRheSA9IHdlZWtkYXlzKGFzLlBPU0lYbHQoam9pbiRpbnRlcnZhbF9zdGFydF90aW1lc3RhbXApKQpgYGAKCiMjIG1ha2UgdGhlIG5ldyBkYXRhZnJhbWUKQWZ0ZXIgbWFraW5nIHRoZSBzcGxpdCBkYXRhZnJhbWVzLCBJIHVzZWQgYSBjYmluZCB0byBhcHBlbmQgdGhlIGNvbHVtbnMgdG8gdGhlIG9yaWdpbmFsIGRhdGFzZXQgYW5kIHJlb3JkZXJlZCB0aGUgZGF0YXNldC4KYGBge3J9CmpvaW5zID0gY2JpbmQoam9pbiwgeWVhciwgbW9udGgsZGF5KQpqb2lucwoKam9pbnMgPSBqb2luc1ssYygxLDUsNiw3LDIsMyw0KV0Kam9pbnMKYGBgCiMjIHRlc3RpbmcgaWYgSSBjb3VsZCBjaGFuZ2UgdGhlIG1vbnRocyB0byBiZWNvbWUgYSBmYWN0b3IKYGBge3J9CiMgdGVzdCB0byBzZWUgd2hhdCB3b3VsZCBoYXBwZW4gaWYgSSBjb3VsZCBjb252ZXJ0IGEgbW9udGhzIG91dHB1dCBhcyBhIGZhY3RvcgpmYWN0b3IobW9udGhzKGFzLlBPU0lYbHQoam9pbiRpbnRlcnZhbF9zdGFydF90aW1lc3RhbXApKSxsZXZlbHMgPSBtb250aC5uYW1lKVsxOjIwXQpgYGAKCiMjIEV4dHJhY3RpbmcgZGF0ZSB0aW1lCnJ1biB0aGUgZm9sbG93aW5nIGNlbGwgdG8gZXh0cmFjdCB5ZWFyLCBtb250aCwgZGF5CgojIyMgam9pbnMgZXh0cmFjdGlvbgpgYGB7cn0KIyBzdWJzdHJpbmcgcmVwbGFjZW1lbnQKam9pblssMV0gPSBnc3ViKCJcXCsuKiIsICdaJywgam9pblssMV0pCgojIGluZGl2aWR1YWwgZXh0cmFjdGlvbgp5ZWFyID0gZmFjdG9yKHllYXIoYXMuUE9TSVhsdChqb2luWywxXSkpKQptb250aCA9IGZhY3Rvcihtb250aHMoYXMuUE9TSVhsdChqb2luWywxXSkpLGxldmVscyA9IG1vbnRoLm5hbWUpCmRheSA9IHdlZWtkYXlzKGFzLlBPU0lYbHQoam9pblssMV0pKQoKIyBhcHBlbmRpbmcgbmV3IGluZGl2dWFsbHkgZXh0cmFjdGVkIGRhdGVzCmpvaW5zID0gY2JpbmQoam9pbiwgeWVhciwgbW9udGgsZGF5KQpqb2lucyA9IGpvaW5zWyxjKDEsNSw2LDcsMiwzLDQpXQpqb2lucwpgYGAKIyMjIHNvdXJjZXMgZXh0cmFjdGlvbgpgYGB7cn0KIyBzdWJzdHJpbmcgcmVwbGFjZW1lbnQKc291cmNlWywxXSA9IGdzdWIoIlxcKy4qIiwgJ1onLCBzb3VyY2VbLDFdKQoKIyBpbmRpdmlkdWFsIGV4dHJhY3Rpb24KeWVhciA9IGZhY3Rvcih5ZWFyKGFzLlBPU0lYbHQoc291cmNlWywxXSkpKQptb250aCA9IGZhY3Rvcihtb250aHMoYXMuUE9TSVhsdChzb3VyY2VbLDFdKSksbGV2ZWxzID0gbW9udGgubmFtZSkKZGF5ID0gd2Vla2RheXMoYXMuUE9TSVhsdChzb3VyY2VbLDFdKSkKCiMgYXBwZW5kaW5nIG5ldyBpbmRpdnVhbGx5IGV4dHJhY3RlZCBkYXRlcwpzb3VyY2VzID0gY2JpbmQoc291cmNlLCB5ZWFyLCBtb250aCxkYXkpCnNvdXJjZXMgPSBzb3VyY2VzWyxjKDEsNSw2LDcsMiwzLDQpXQpzb3VyY2VzCmBgYAojIyMgbGVhdmVzIGV4dHJhY3Rpb24KYGBge3J9CiMgc3Vic3RyaW5nIHJlcGxhY2VtZW50CmxlYXZlWywxXSA9IGdzdWIoIlxcKy4qIiwgJ1onLCBsZWF2ZVssMV0pCgojIGluZGl2aWR1YWwgZXh0cmFjdGlvbgp5ZWFyID0gZmFjdG9yKHllYXIoYXMuUE9TSVhsdChsZWF2ZVssMV0pKSkKbW9udGggPSBmYWN0b3IobW9udGhzKGFzLlBPU0lYbHQobGVhdmVbLDFdKSksbGV2ZWxzID0gbW9udGgubmFtZSkKZGF5ID0gd2Vla2RheXMoYXMuUE9TSVhsdChsZWF2ZVssMV0pKQoKIyBhcHBlbmRpbmcgbmV3IGluZGl2dWFsbHkgZXh0cmFjdGVkIGRhdGVzCmxlYXZlCmxlYXZlcyA9IGNiaW5kKGxlYXZlLCB5ZWFyLCBtb250aCxkYXkpCmxlYXZlcwpsZWF2ZXMgPSBsZWF2ZXNbLGMoMSw0LDUsNiwyLDMpXQpsZWF2ZXMKYGBgCgojIyMgbWVzc2FnZXMgZXh0cmFjdGlvbgpgYGB7cn0KIyBzdWJzdHJpbmcgcmVwbGFjZW1lbnQKbWVzc2FnZVssMV0gPSBnc3ViKCJcXCsuKiIsICdaJywgbWVzc2FnZVssMV0pCgojIGluZGl2aWR1YWwgZXh0cmFjdGlvbgp5ZWFyID0gZmFjdG9yKHllYXIoYXMuUE9TSVhsdChtZXNzYWdlWywxXSkpKQptb250aCA9IGZhY3Rvcihtb250aHMoYXMuUE9TSVhsdChtZXNzYWdlWywxXSkpLGxldmVscyA9IG1vbnRoLm5hbWUpCmRheSA9IHdlZWtkYXlzKGFzLlBPU0lYbHQobWVzc2FnZVssMV0pKQoKIyBhcHBlbmRpbmcgbmV3IGluZGl2dWFsbHkgZXh0cmFjdGVkIGRhdGVzCm1lc3NhZ2VzID0gY2JpbmQobWVzc2FnZSwgeWVhciwgbW9udGgsZGF5KQptZXNzYWdlcwptZXNzYWdlcyA9IG1lc3NhZ2VzWyxjKDEsNCw1LDYsMiwzKV0KbWVzc2FnZXMKYGBgCgojIyMgdm9pY2VzIGV4dHJhY3Rpb24KYGBge3J9CiMgc3Vic3RyaW5nIHJlcGxhY2VtZW50CnZvaWNlWywxXSA9IGdzdWIoIlxcKy4qIiwgJ1onLCB2b2ljZVssMV0pCgojIGluZGl2aWR1YWwgZXh0cmFjdGlvbgp5ZWFyID0gZmFjdG9yKHllYXIoYXMuUE9TSVhsdCh2b2ljZVssMV0pKSkKbW9udGggPSBmYWN0b3IobW9udGhzKGFzLlBPU0lYbHQodm9pY2VbLDFdKSksbGV2ZWxzID0gbW9udGgubmFtZSkKZGF5ID0gd2Vla2RheXMoYXMuUE9TSVhsdCh2b2ljZVssMV0pKQoKIyBhcHBlbmRpbmcgbmV3IGluZGl2dWFsbHkgZXh0cmFjdGVkIGRhdGVzCnZvaWNlcyA9IGNiaW5kKHZvaWNlLCB5ZWFyLCBtb250aCxkYXkpCnZvaWNlcyA9IHZvaWNlc1ssYygxLDMsNCw1LDIpXQp2b2ljZXMKYGBgCiMjIyBjb21tdW5pY2F0b3JzIGV4dHJhY3Rpb24KYGBge3J9CiMgc3Vic3RyaW5nIHJlcGxhY2VtZW50CmNvbW11bmljYXRvclssMV0gPSBnc3ViKCJcXCsuKiIsICdaJywgY29tbXVuaWNhdG9yWywxXSkKCiMgaW5kaXZpZHVhbCBleHRyYWN0aW9uCnllYXIgPSBmYWN0b3IoeWVhcihhcy5QT1NJWGx0KGNvbW11bmljYXRvclssMV0pKSkKbW9udGggPSBmYWN0b3IobW9udGhzKGFzLlBPU0lYbHQoY29tbXVuaWNhdG9yWywxXSkpLGxldmVscyA9IG1vbnRoLm5hbWUpCmRheSA9IHdlZWtkYXlzKGFzLlBPU0lYbHQoY29tbXVuaWNhdG9yWywxXSkpCmNvbW11bmljYXRvcgoKIyBhcHBlbmRpbmcgbmV3IGluZGl2aWR1YWxseSBleHRyYWN0ZWQgZGF0ZXMKY29tbXVuaWNhdG9ycyA9IGNiaW5kKGNvbW11bmljYXRvciwgeWVhciwgbW9udGgsZGF5KQpjb21tdW5pY2F0b3JzID0gY29tbXVuaWNhdG9yc1ssYygxLDQsNSw2LDIsMyldCmNvbW11bmljYXRvcnMkdG90YWxfY29tbXVuaWNhdGVkID0gY29tbXVuaWNhdG9ycyR2aXNpdG9ycyAqIGNvbW11bmljYXRvcnMkcGN0X2NvbW11bmljYXRlZC8xMDAKYGBgCiMjIEFkZGl0aW9uYWwgbW9kaWZpY2F0aW9ucwpUaGUgZm9sbG93aW5nIG1vZGlmaWNhdGlvbnMgYXJlIG15IGF0dGVtcHRzIHRvIGlkZW50aWZ5IGNvdmlkIHllYXJzIGZvciBvdXIgYW5hbHlzaXMsIEkgY291bGQgZWRpdCB0aGUgY3N2LCBidXQgSSBkZWNpZGVkIHRvIGV4cGxvcmUgUiB0byBwcmFjdGljZSBldGwgZm9yIGxhcmdlciBkYXRhc2V0cy4gVGhlIEZhbGwgMjAxNyBTVEFUIDIwMCBjb3Vyc2UgcGFnZSBvbiBbUmVncmVzc2lvbiBXaXRoIEZhY3RvciBWYXJpYWJsZXNdKGh0dHA6Ly9jb3Vyc2VzLmF0bGFzLmlsbGlub2lzLmVkdS9mYWxsMjAxNy9TVEFUL1NUQVQyMDAvUlByb2dyYW1taW5nL1JlZ3Jlc3Npb25GYWN0b3JzLmh0bWwpIHdhcyBwYXJ0aWN1bGFybHkgaGVscGZ1bCBhcyBhIHJlZmVyZW5jZSB3aGVuIEkgd2FzIHRyeWluZyB0byBoYXZlIFIgdXNlIGBDb3ZpZGAgYXMgdGhlIGRlZmF1bHQgZmFjdG9yIGluc3RlYWQgb2YgYE5vcm1hbGAsIGhhdmluZyBgQ292aWRgIGFzIHRoZSBkZWZhdWx0IGZhY3RvciB3aWxsIGJlIGltcG9ydGFudCB3aGVuIEkgZ2VuZXJhdGUgdGhlIGxpbmVhciBtb2RlbHMgYW5kIGludGVycHJldCB0aGUgb3V0cHV0cy4gSSB3b3VsZCBhbHNvIHJlY29tbWVuZCByZWFkaW5nIHRoZSBiZXJrbGV5IHN0YXRzIHBhZ2Ugb24gWyJGYWN0b3JzIGluIFIiXShodHRwczovL3d3dy5zdGF0LmJlcmtlbGV5LmVkdS9+czEzMy9mYWN0b3JzLmh0bWwpIHRvIGdldCBhIGRlZXBlciB1bmRlcnN0YW5kaW5nIG9mIGhvdyB0byBjb252ZXJ0IGZhY3RvcnMgd2l0aCBkYXRlcyAKCkkgY291bGQgaGF2ZSBhcHBsaWVkIHRoZSBgcmVsZXZlbCgpYCB0byB0aGUgYGFzLmZhY3RvcmAgbGluZSBhcyBzZWVuIGluIHRoaXMgc3RhY2sgb3ZlcmZsb3cgYW5zd2VyIFtIb3cgdG8gZm9yY2UgUiB0byB1c2UgYSBzcGVjaWZpZWQgZmFjdG9yIGxldmVsIGFzIHJlZmVyZW5jZSBpbiBhIHJlZ3Jlc3Npb24/XShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvNDc4MTU3MDkpLCBidXQgSSByZWFsaXplZCBpdCB3YXMgbXVjaCBlYXNpZXIgdG8gcmVhZC9ydW4gdGhlIGNvZGUgaW4gbXkgaGVhZCBsaW5lIGJ5IGxpbmUgdGhhbiB0byBwYXNzIGludG8gbXVsdGlwZSBmdW5jdGlvbnMKYGBge3J9CiMgbWFya2luZyBjb3ZpZCBhbmQgbm9uIGNvdmlkIG1vbnRocwpqb2lucyR5ZWFyX3R5cGUgPSBhcy5kb3VibGUoam9pbnMkeWVhcikKam9pbnMkeWVhcl90eXBlW2pvaW5zJHllYXJfdHlwZSA9PSAxIF0gPC0gIk5vcm1hbCIKam9pbnMkeWVhcl90eXBlW2pvaW5zJHllYXJfdHlwZSA9PSAyXSA8LSAiQ292aWQiCmpvaW5zJHllYXJfdHlwZVtqb2lucyR5ZWFyX3R5cGUgPT0gM10gPC0gIkNvdmlkIgpqb2lucyR5ZWFyX3R5cGUgPSBhcy5mYWN0b3Ioam9pbnMkeWVhcl90eXBlKQpqb2lucyR5ZWFyX3R5cGUgPSByZWxldmVsKGpvaW5zJHllYXJfdHlwZSwgcmVmID0gMikKam9pbnMKCmxlYXZlcyR5ZWFyX3R5cGUgPSBhcy5kb3VibGUobGVhdmVzJHllYXIpCmxlYXZlcyR5ZWFyX3R5cGVbbGVhdmVzJHllYXJfdHlwZSA9PSAxIF0gPC0gIk5vcm1hbCIKbGVhdmVzJHllYXJfdHlwZVtsZWF2ZXMkeWVhcl90eXBlID09Ml0gPC0gIkNvdmlkIgpsZWF2ZXMkeWVhcl90eXBlW2xlYXZlcyR5ZWFyX3R5cGUgPT0zXSA8LSAiQ292aWQiCmxlYXZlcyR5ZWFyX3R5cGUgPSBhcy5mYWN0b3IobGVhdmVzJHllYXJfdHlwZSkKbGVhdmVzJHllYXJfdHlwZSA9IHJlbGV2ZWwobGVhdmVzJHllYXJfdHlwZSwgcmVmID0gMikKbGVhdmVzCgpzb3VyY2VzJHllYXJfdHlwZSA9IGFzLmRvdWJsZShzb3VyY2VzJHllYXIpCnNvdXJjZXMkeWVhcl90eXBlW3NvdXJjZXMkeWVhcl90eXBlID09IDEgXSA8LSAiTm9ybWFsIgpzb3VyY2VzJHllYXJfdHlwZVtzb3VyY2VzJHllYXJfdHlwZSA9PTJdIDwtICJDb3ZpZCIKc291cmNlcyR5ZWFyX3R5cGVbc291cmNlcyR5ZWFyX3R5cGUgPT0zXSA8LSAiQ292aWQiCnNvdXJjZXMkeWVhcl90eXBlID0gYXMuZmFjdG9yKHNvdXJjZXMkeWVhcl90eXBlKQpzb3VyY2VzJHllYXJfdHlwZSA9IHJlbGV2ZWwoc291cmNlcyR5ZWFyX3R5cGUsIHJlZiA9IDIpCnNvdXJjZXMKCm1lc3NhZ2VzJHllYXJfdHlwZSA9IGFzLmRvdWJsZShtZXNzYWdlcyR5ZWFyKQptZXNzYWdlcyR5ZWFyX3R5cGVbbWVzc2FnZXMkeWVhcl90eXBlID09IDEgXSA8LSAiTm9ybWFsIgptZXNzYWdlcyR5ZWFyX3R5cGVbbWVzc2FnZXMkeWVhcl90eXBlID09Ml0gPC0gIkNvdmlkIgptZXNzYWdlcyR5ZWFyX3R5cGVbbWVzc2FnZXMkeWVhcl90eXBlID09M10gPC0gIkNvdmlkIgptZXNzYWdlcyR5ZWFyX3R5cGUgPSBhcy5mYWN0b3IobWVzc2FnZXMkeWVhcl90eXBlKQptZXNzYWdlcyR5ZWFyX3R5cGUgPSByZWxldmVsKG1lc3NhZ2VzJHllYXJfdHlwZSwgcmVmID0gMikKbWVzc2FnZXMKCnZvaWNlcyR5ZWFyX3R5cGUgPSBhcy5kb3VibGUodm9pY2VzJHllYXIpCnZvaWNlcyR5ZWFyX3R5cGVbdm9pY2VzJHllYXJfdHlwZSA9PSAxIF0gPC0gIk5vcm1hbCIKdm9pY2VzJHllYXJfdHlwZVt2b2ljZXMkeWVhcl90eXBlID09Ml0gPC0gIkNvdmlkIgp2b2ljZXMkeWVhcl90eXBlW3ZvaWNlcyR5ZWFyX3R5cGUgPT0zXSA8LSAiQ292aWQiCnZvaWNlcyR5ZWFyX3R5cGUgPSBhcy5mYWN0b3Iodm9pY2VzJHllYXJfdHlwZSkKdm9pY2VzJHllYXJfdHlwZSA9IHJlbGV2ZWwodm9pY2VzJHllYXJfdHlwZSwgcmVmID0gMikKdm9pY2VzCgpjb21tdW5pY2F0b3JzJHllYXJfdHlwZSA9IGFzLmRvdWJsZShjb21tdW5pY2F0b3JzJHllYXIpCmNvbW11bmljYXRvcnMkeWVhcl90eXBlW2NvbW11bmljYXRvcnMkeWVhcl90eXBlID09IDEgXSA8LSAiTm9ybWFsIgpjb21tdW5pY2F0b3JzJHllYXJfdHlwZVtjb21tdW5pY2F0b3JzJHllYXJfdHlwZSA9PTJdIDwtICJDb3ZpZCIKY29tbXVuaWNhdG9ycyR5ZWFyX3R5cGVbY29tbXVuaWNhdG9ycyR5ZWFyX3R5cGUgPT0zXSA8LSAiQ292aWQiCmNvbW11bmljYXRvcnMkeWVhcl90eXBlID0gYXMuZmFjdG9yKGNvbW11bmljYXRvcnMkeWVhcl90eXBlKQpjb21tdW5pY2F0b3JzJHllYXJfdHlwZSA9IHJlbGV2ZWwoY29tbXVuaWNhdG9ycyR5ZWFyX3R5cGUsIHJlZiA9IDIpCmNvbW11bmljYXRvcnMKYGBgCgoKIyBkYXRhIG5lZWRlZCBmb3IgaW52ZXN0aWdhdGlvbgojIyBoaXN0b3JpY2FsIGRhdGEKYGBge3J9CmpvaW5zCmxlYXZlcwpzb3VyY2VzCm1lc3NhZ2VzCnZvaWNlcwpjb21tdW5pY2F0b3JzCmBgYAojIyBsYXN0IDI4IGRheXMKYGBge3J9CnRleHQKdm9pY2UKYGBgCiMgc3Vic2V0dGluZyBieSB5ZWFyCgpPcmlnaW5hbGx5IEkgcGxhbm5lZCBvbiBhZ2dyZWdhdGluZyBieSB0aGUgeWVhciBmb3IgbXkgYmFyIGNoYXJ0cywgYnV0IHdoZW4gSSByZWFkIHRocm91Z2ggc29tZSBtb3JlIGV4YW1wbGVzIG9mIGFnZ3JlZ2F0ZXMsIEkgZm91bmQgYSBiZXR0ZXIgbWV0aG9kIGluICJBZ2dyZWdhdGluZyBieSBjYXRlZ29yeSIKYGBge3J9CmpvaW5zLjIwMTkgPSBzdWJzZXQoam9pbnMsIHllYXIgPT0gMjAxOSkKam9pbnMuMjAyMCA9IHN1YnNldChqb2lucywgeWVhciA9PSAyMDIwKQpqb2lucy4yMDIxID0gc3Vic2V0KGpvaW5zLCB5ZWFyID09IDIwMjEpCgpsZWF2ZXMuMjAxOSA9IHN1YnNldChsZWF2ZXMsIHllYXIgPT0gMjAxOSkKbGVhdmVzLjIwMjAgPSBzdWJzZXQobGVhdmVzLCB5ZWFyID09IDIwMjApCmxlYXZlcy4yMDIxID0gc3Vic2V0KGxlYXZlcywgeWVhciA9PSAyMDIxKQoKc291cmNlcy4yMDE5ID0gc3Vic2V0KHNvdXJjZXMsIHllYXIgPT0gMjAxOSkKc291cmNlcy4yMDIwID0gc3Vic2V0KHNvdXJjZXMsIHllYXIgPT0gMjAyMCkKc291cmNlcy4yMDIxID0gc3Vic2V0KHNvdXJjZXMsIHllYXIgPT0gMjAyMSkKCmNvbW0uMjAxOSA9IHN1YnNldChjb21tdW5pY2F0b3JzLCB5ZWFyID09IDIwMTkpCmNvbW0uMjAyMCA9IHN1YnNldChjb21tdW5pY2F0b3JzLCB5ZWFyID09IDIwMjApCmNvbW0uMjAyMSA9IHN1YnNldChjb21tdW5pY2F0b3JzLCB5ZWFyID09IDIwMjEpCmBgYAoKIyBBZ2dyZWdhdGluZyBieSB5ZWFyCiMjIDIwMTkKYGBge3J9CmpvaW5zLjIwMTkKbGVhdmVzLjIwMTkKc291cmNlcy4yMDE5CmNvbW0uMjAxOQpgYGAKCiMjIDIwMjAKYGBge3J9CmpvaW5zLjIwMjAKbGVhdmVzLjIwMjAKc291cmNlcy4yMDIwCmNvbW0uMjAyMApgYGAKCiMjIDIwMjEKYGBge3J9CmpvaW5zLjIwMjEKbGVhdmVzLjIwMjEKc291cmNlcy4yMDIxCmNvbW0uMjAyMQpgYGAKCiMgQWdncmVnYXRpbmcgYnkgbW9udGgKIyMgMjAxOQpgYGB7cn0Kam9pbnMuMjAxOQpsZWF2ZXMuMjAxOQpjb21tLjIwMTkKCmFnZ19qb2lucy4yMDE5ID0gYWdncmVnYXRlKGpvaW5zLjIwMTkkbmV3X21lbWJlcnMsIGxpc3Qoam9pbnMuMjAxOSRtb250aCksIHN1bSkKY29sbmFtZXMoYWdnX2pvaW5zLjIwMTkpIDwtIGMoIk1vbnRocyIsICJUb3RhbCBOZXcgTWVtYmVycyIpCmFnZ19sZWF2ZXMuMjAxOSA9IGFnZ3JlZ2F0ZShsZWF2ZXMuMjAxOSRsZWF2ZXJzLCBsaXN0KGxlYXZlcy4yMDE5JG1vbnRoKSwgc3VtKQpjb2xuYW1lcyhhZ2dfbGVhdmVzLjIwMTkpIDwtIGMoIk1vbnRocyIsICJUb3RhbCBMZWF2ZXJzIikKYWdnX2NvbW0uMjAxOSA9IGFnZ3JlZ2F0ZShjb21tLjIwMTkkdG90YWxfY29tbXVuaWNhdGVkLCBsaXN0KGNvbW0uMjAxOSRtb250aCksIHN1bSkKY29sbmFtZXMoYWdnX2NvbW0uMjAxOSkgPC0gYygiTW9udGhzIiwgIlRvdGFsIENvbW11bmljYXRlZCIpCgphZ2dfam9pbnMuMjAxOVtvcmRlcihtZWRfam9pbnMuMjAxOSR4KSxdCmFnZ19sZWF2ZXMuMjAxOVtvcmRlcihtZWRfbGVhdmVzLjIwMTkkeCksXQphZ2dfY29tbS4yMDE5W29yZGVyKG1lZF9jb21tLjIwMTkkeCksXQpgYGAKCiMjIDIwMjAKYGBge3J9CmpvaW5zLjIwMjAKbGVhdmVzLjIwMjAKY29tbS4yMDIwCgphZ2dfam9pbnMuMjAyMCA9IGFnZ3JlZ2F0ZShqb2lucy4yMDIwJG5ld19tZW1iZXJzLCBsaXN0KGpvaW5zLjIwMjAkbW9udGgpLCBzdW0pCmNvbG5hbWVzKGFnZ19qb2lucy4yMDIwKSA8LSBjKCJNb250aHMiLCAiVG90YWwgTmV3IE1lbWJlcnMiKQphZ2dfbGVhdmVzLjIwMjAgPSBhZ2dyZWdhdGUobGVhdmVzLjIwMjAkbGVhdmVycywgbGlzdChsZWF2ZXMuMjAyMCRtb250aCksIHN1bSkKY29sbmFtZXMoYWdnX2xlYXZlcy4yMDIwKSA8LSBjKCJNb250aHMiLCAiVG90YWwgTGVhdmVycyIpCmFnZ19jb21tLjIwMjAgPSBhZ2dyZWdhdGUoY29tbS4yMDIwJHRvdGFsX2NvbW11bmljYXRlZCwgbGlzdChjb21tLjIwMjAkbW9udGgpLCBzdW0pCmNvbG5hbWVzKGFnZ19jb21tLjIwMjApIDwtIGMoIk1vbnRocyIsICJUb3RhbCBDb21tdW5pY2F0ZWQiKQoKCmFnZ19qb2lucy4yMDIwW29yZGVyKG1lZF9qb2lucy4yMDIwJHgpLF0KYWdnX2xlYXZlcy4yMDIwW29yZGVyKG1lZF9sZWF2ZXMuMjAyMCR4KSxdCmFnZ19jb21tLjIwMjBbb3JkZXIobWVkX2NvbW0uMjAyMCR4KSxdCmBgYAojIyAyMDIxCmBgYHtyfQpqb2lucy4yMDIxCmxlYXZlcy4yMDIxCmNvbW0uMjAyMQoKYWdnX2pvaW5zLjIwMjEgPSBhZ2dyZWdhdGUoam9pbnMuMjAyMSRuZXdfbWVtYmVycywgbGlzdChqb2lucy4yMDIxJG1vbnRoKSwgc3VtKQpjb2xuYW1lcyhhZ2dfam9pbnMuMjAyMSkgPC0gYygiTW9udGhzIiwgIlRvdGFsIE5ldyBNZW1iZXJzIikKYWdnX2xlYXZlcy4yMDIxID0gYWdncmVnYXRlKGxlYXZlcy4yMDIxJGxlYXZlcnMsIGxpc3QobGVhdmVzLjIwMjEkbW9udGgpLCBzdW0pCmNvbG5hbWVzKGFnZ19sZWF2ZXMuMjAyMSkgPC0gYygiTW9udGhzIiwgIlRvdGFsIExlYXZlcnMiKQphZ2dfY29tbS4yMDIxID0gYWdncmVnYXRlKGNvbW0uMjAyMSR0b3RhbF9jb21tdW5pY2F0ZWQsIGxpc3QoY29tbS4yMDIxJG1vbnRoKSwgc3VtKQpjb2xuYW1lcyhhZ2dfY29tbS4yMDIxKSA8LSBjKCJNb250aHMiLCAiVG90YWwgQ29tbXVuaWNhdGVkIikKCgoKYWdnX2pvaW5zLjIwMjFbb3JkZXIobWVkX2pvaW5zLjIwMjEkeCksXQphZ2dfbGVhdmVzLjIwMjFbb3JkZXIobWVkX2xlYXZlcy4yMDIxJHgpLF0KYWdnX2NvbW0uMjAyMVtvcmRlcihtZWRfY29tbS4yMDIxJHgpLF0KYGBgCiMjIHRlc3RpbmcgYWdncmVnYXRpb25zCmBgYHtyfQpjb21tdW5pY2F0b3JzCm1lZGlhbl9jb21tID0gYWdncmVnYXRlKGNvbW11bmljYXRvcnMkdmlzaXRvcnMsIGxpc3QoY29tbXVuaWNhdG9ycyRtb250aCksIHN1bSkKbWVkaWFuX2NvbW1bb3JkZXIobWVkaWFuX2NvbW0keCksXQpgYGAKCiMgQWdncmVnYXRpbmcgYnkgY2F0ZWdvcnkKQXMgbWVudGlvbmVkIGluIHRoZSBzdWJzZXR0aW5nIGJ5IHllYXIgc2VjdGlvbiwgdXBvbiByZWFkaW5nIHNvbWUgZXhhbXBsZXMgZm9yIGFnZ3JlZ2F0aW5nIGluIFIsIEkgZm91bmQgdGhhdCB0aGVyZSB3YXMgYSBtZXRob2QgdG8gYWdncmVnYXRlIGJ5IG11bHRpcGxlIGNvbHVtbnMuIFRoZSBmb2xsb3dpbmcgYXJ0aWNsZSBbIkFnZ3JlZ2F0ZSBpbiBSIl0oaHR0cHM6Ly9yLWNvZGVyLmNvbS9hZ2dyZWdhdGUtci8pIHdhcyBwYXJ0aWN1bGFybHkgaGVscGZ1bCBhcyBpdCBoYWQgc2FtcGxlIGNvZGUgd2l0aCB1c2VmdWwgb3V0cHV0cy4gVGhlIHNlY29uZCBvcHRpb24gb2YgdXNpbmcgUiBsaW5lYXIgbW9kZWwgbm90YXRpb24gaXMgYSBiaXQgbW9yZSBpbnR1aXRpdmUgdGhhbiB0aGUgZmlyc3Qgc3VnZ2VzdGlvbi4KCmBgYAphZ2dyZWdhdGUoZGZfMiR3ZWlnaHQsIGJ5ID0gbGlzdChkZl8yJGZlZWQsIGRmXzIkY2F0X3ZhciksIEZVTiA9IHN1bSkKCiMgRXF1aXZhbGVudCB0bzoKYWdncmVnYXRlKHdlaWdodCB+IGZlZWQgKyBjYXRfdmFyLCBkYXRhID0gZGZfMiwgRlVOID0gc3VtKQpgYGAKCiMjIGpvaW5zCmBgYHtyfQpqb2lucwphZ2dfam9pbnMgPSBhZ2dyZWdhdGUobmV3X21lbWJlcnMgfiBtb250aCArIHllYXIsIGRhdGEgPSBqb2lucywgRlVOID0gc3VtKQphZ2dfam9pbnMKYGBgCiMjIGxlYXZlcwpgYGB7cn0KbGVhdmVzCmFnZ19sZWF2ZXMgPSBhZ2dyZWdhdGUobGVhdmVycyB+IG1vbnRoICsgeWVhciwgZGF0YSA9IGxlYXZlcywgRlVOID0gc3VtKQphZ2dfbGVhdmVzCmBgYAoKIyMjIGV4cGVyaW1lbnRhbCAzZCBhZ2cKYGBge3J9CmxlYXZlcwphZ2dfbGVhdmVzID0gYWdncmVnYXRlKGxlYXZlcnMgfiBtb250aCArIHllYXIsIGRhdGEgPSBsZWF2ZXMsIEZVTiA9IHN1bSkKYWdnX2xlYXZlcwpgYGAKCiMjIHNvdXJjZXMKbG9va3MgcmVhbGx5IHdlaXJkIGlnbm9yaW5nIGZvciBub3cKYGBge3J9CnNvdXJjZXMKYWdnX3NvdXJjZXMgPSBhZ2dyZWdhdGUoZGlzY292ZXJ5X2pvaW5zICsgaW52aXRlcyArIHZhbml0eV9qb2lucyB+IG1vbnRoICsgeWVhciwgZGF0YSA9IHNvdXJjZXMsIEZVTiA9IHN1bSkKYWdnX3NvdXJjZXMKYGBgCgojIyBjb21tcwpgYGB7cn0KY29tbXVuaWNhdG9ycwphZ2dfY29tbXMgPSBhZ2dyZWdhdGUodG90YWxfY29tbXVuaWNhdGVkIH4gbW9udGggKyB5ZWFyLCBkYXRhID0gY29tbXVuaWNhdG9ycywgRlVOID0gc3VtKQphZ2dfY29tbXMKYGBgCgoKIyBWaXN1YWxpemF0aW9ucwpJIHJlYWxpemVkIHRoYXQgdXNpbmcgUidzIGJhc2UgcGxvdHMgd2VyZSBub3QgZ29pbmcgdG8gbWFrZSB0aGUgY3V0LiBJIHJlY2FsbCB0aGF0IHdoZW4gSSB3YXMgc2VhcmNoaW5nIGZvciBncmFwaGluZyBzb2x1dGlvbnMgb24gYSBkaWZmZXJlbnQgcHJvamVjdCwgSSBmb3VuZCBhbiBhcHBlYWxpbmcgZ3JhcGggc29sdXRpb24gd2l0aCBnZ3Bsb3RzLiBBdCB0aGUgdGltZSBJIHdhcyB1c2luZyBweXRob24sIHNvIGdncGxvdCB3YXNuJ3QgYSBsaWJyYXJ5IHN1cHBvcnRlZC4gSW4gYW5vdGhlciBjbGFzcywgdGhlIHByb2Zlc3NvciBpbnRyb2R1Y2VkIGdncGxvdHMuIEkgY291bGQgaGF2ZSB1c2VkIGV4Y2VsIHRvIGdlbmVyYXRlIHRoZSBwbG90cywgYnV0IEkgd2FudGVkIGEgbGVhcm5pbmcgb3Bwb3J0dW5pdHkgdG8gdHJ5IGdncGxvdCBvbiBzb21ldGhpbmcgdGhhdCB3YXNuJ3QgaG9tZXdvcmsgb3IgY2xhc3N3b3JrLiBJIGtuZXcgSSBuZWVkZWQgYSBzdGFja2VkIGJhciBjaGFydCBhcyBJIG5lZWRlZCB0byBjb21wYXJlIHRoZSBjaGFuZ2VzIGFjcm9zcyB0aGUgbW9udGhzIGFuZCB5ZWFycy4KCkFmdGVyIGEgc2VhcmNoIG9uIHRoZSB3ZWIsIEkgZm91bmQgdGhlIGZvbGxvd2luZyBndWlkZSBbIkhvdyB0byBDcmVhdGUgYW5kIEN1c3RvbWl6ZSBCYXIgUGxvdCBVc2luZyBnZ3Bsb3QyIFBhY2thZ2UgaW4gUi0gT25lIFplcm8gQmxvZyJdKGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS9ob3ctdG8tY3JlYXRlLWFuZC1jdXN0b21pemUtYmFyLXBsb3QtdXNpbmctZ2dwbG90Mi1wYWNrYWdlLWluLXItNDg3MjAwNDg3OGE3KSBvbiB0aGUgdG93YXJkcyBkYXRhIHNjaWVuY2UgbWVkaXVtIHRvIGJlIHBhcnRpY3VsYXJseSBoZWxwZnVsLCBhcyB0aGVyZSB3YXMgc2FtcGxlIGNvZGUgd2l0aCBvdXRwdXRzLiBJIHVzZWQgdGhlIHNhbXBsZSBjb2RlIGZyb20gc2VjdGlvbiBvbiBiYXIgbGFiZWxzIG9uIGEgc3RhY2sgYmFyIHBsb3QgYXMgYSBiYXNlIGFuZCBtYWRlIG1vZGlmaWNhdGlvbnMgdG8gZml0IG15IGRhdGEuCgojIyBhbGwgam9pbnMKVG8gbWFrZSBpdCBlYXNpZXIgZm9yIG1lIHRvIGlucHV0IHRoZSBwYXJhbWV0ZXJzLCBJIGxvYWRlZCBhbGwgdGhlIGFnZ3JlZ2F0ZSBkYXRhLCBzaW5jZSBJIHdhc24ndCBzdXJlIGhvdyB0aGUgZ3JhcGhzIHdvdWxkIGxvb2suCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCgpqb2lucwphZ2dfam9pbnMuMjAxOQphZ2dfam9pbnMuMjAyMAphZ2dfam9pbnMuMjAyMQphZ2dfam9pbnMKYGBgCgpJIHN0YXJ0ZWQgYnkgc3Vic3RpdHV0aW5nIHRoZSBzYW1wbGUgcGFyYW1ldGVycyB3aXRoIG15IG93biBkYXRhc2V0LiBJIHF1aWNrbHkgcmVhbGl6ZWQgdGhhdCB0aGUgZ3JhcGggaGFkIHNvbWUgaXNzdWVzIG9uIHRoZSB4IGF4aXMuIFRoZSBtb250aCBuYW1lcyB3ZXJlIG92ZXJsYXBwaW5nLiAKYGBge3J9CmFsbF9qb2lucyA9IGdncGxvdChkYXRhID0gYWdnX2pvaW5zLCBtYXBwaW5nID0gYWVzKHggPSBtb250aCwgeSA9IG5ld19tZW1iZXJzLCBmaWxsID0geWVhcikpICsgeGxhYigiTW9udGgiKSArIHlsYWIoIlRvdGFsIE5ldyBNZW1iZXJzIikgKyBnZW9tX2NvbCgpKyAKICAgICAgICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1uZXdfbWVtYmVycyksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3Q9IDAuNSksCiAgICAgICAgICAgIGNvbG91ciA9ICJ3aGl0ZSIsIHNpemUgPSA1KQphbGxfam9pbnMgPSBhbGxfam9pbnMgKyBsYWJzKHRpdGxlID0gIk5ldyBNZW1iZXIgSm9pbnMgQWNyb3NzIHRoZSBZZWFyIikKYWxsX2pvaW5zCmBgYApBZnRlciBzZWFyY2hpbmcgdGhlIHdlYiwgSSBmb3VuZCBhIGdyZWF0IHN0YWNrIG92ZXJmbG93IGFuc3dlciBbSG93IHRvIG1haW50YWluIHNpemUgb2YgZ2dwbG90IHdpdGggbG9uZyBsYWJlbHNdKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS80MTYwNzIwMSkgdGhhdCB1bHRpbWF0ZWx5IGluZmx1ZW5jZWQgdGhlIGZpbmFsIGdyYXBocy4gCmBgYHtyfQphbGxfam9pbnMgPSBnZ3Bsb3QoZGF0YSA9IGFnZ19qb2lucywgbWFwcGluZyA9IGFlcyh4ID0gbW9udGgsIHkgPSBuZXdfbWVtYmVycywgZmlsbCA9IHllYXIpKSArIHhsYWIoIk1vbnRoIikgKyB5bGFiKCJUb3RhbCBOZXcgTWVtYmVycyIpICsgZ2VvbV9jb2woKSsgCiAgICAgICAgICAgIGdlb21fdGV4dChhZXMobGFiZWw9bmV3X21lbWJlcnMpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0PSAwLjUpLAogICAgICAgICAgICBjb2xvdXIgPSAid2hpdGUiLCBzaXplID0gNSkgKyBjb29yZF9mbGlwKCkKYWxsX2pvaW5zID0gYWxsX2pvaW5zICsgbGFicyh0aXRsZSA9ICJOZXcgTWVtYmVyIEpvaW5zIEFjcm9zcyB0aGUgWWVhciIpCmFsbF9qb2lucwpgYGAKCldoZW4gSSBmaXJzdCBtYWRlIHRoZSBncmFwaHMsIHRoZSBvcmRlciBvZiB0aGUgeCBheGlzIHdhcyBiYWNrd2FyZHMgZnJvbSBhIG5vcm1hbCB5ZWFyLiBGb3IgdGhlIHByZXNlbnRhdGlvbiBJIHVzZWQgdGhlIHZlcnNpb24gYWJvdmUsIGJ1dCB3aGVuIEkgY2FtZSBiYWNrIGZvciB0aGUgZmluYWwgcmVwb3J0IGFuZCBmaW5hbCB3cml0ZSB1cCwgSSBkZWNpZGVkIHRvIHNlYXJjaCBmb3IgYSBzb2x1dGlvbi4gSSBrbmV3IHByZXZpb3VzbHkgdGhhdCBgY29vcmRfZmxpcCgpYCB3YXMgdGhlIGNhdXNlIG9mIHRoZSBpbml0aWFsIHJldmVyc2VkIG9yZGVyLiBTZWFyY2hpbmcgW2dncGxvdCBjb29yZF9mbGlwKCkgY2hhbmdlIG9yZGVyIG9mIHggYXhpc10oaHR0cHM6Ly9kdWNrZHVja2dvLmNvbS8/cT1nZ3Bsb3QrY29vcmRfZmxpcCgpK2NoYW5nZStvcmRlcitvZit4K2F4aXMmdD1mZmFiJmlhPXdlYikgZm91bmQgdGhlIGFuc3dlciBJIHdhcyBsb29raW5nIGZvci4gVGhlIGZvbGxvd2luZyBhbnN3ZXIgZnJvbSBbUmV2ZXJzZWQgb3JkZXIgYWZ0ZXIgY29vcmRfZmxpcCBpbiBSXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvMzQyNzEwNjApIHdhcyBoYWQgdGhlIHNvbHV0aW9uIEkgd2FzIGxvb2tpbmcgZm9yLiBJIGxlYXJuZWQgdGhhdCBJIGNvdWxkIHVzZSBhIGxpbWl0cyBwYXJhbWV0ZXIgdG8gY2hhbmdlIHRoZSBvcmRlciwgYXMgcGFzc2luZyBgc2NhbGVfeF9kaXNjcmV0ZSgpYCB3aXRoIG91dCBhbnkgcGFyYW1ldGVycyB3b3VsZG4ndCBjaGFuZ2UgbXkgZ3JhcGguCgpVbHRpbWF0ZWx5IHRoaXMgaXMgdGhlIGZpbmFsIHZlcnNpb24gb2YgdGhlIGdyYXBoLiBGb3IgdGhlIHJlcG9ydCwgSSBzY2FsZWQgdGhlIGhvcml6b250YWwgZGltZW5zaW9uIHRvIGJlIDE5MjAgYW5kIGhhZCB0aGUgYXNwZWN0IHJhdGlvIGZpeGVkLgpgYGB7cn0KYWxsX2pvaW5zID0gZ2dwbG90KGRhdGEgPSBhZ2dfam9pbnMsIG1hcHBpbmcgPSBhZXMoeCA9IG1vbnRoLCB5ID0gbmV3X21lbWJlcnMsIGZpbGwgPSB5ZWFyKSkgKyB4bGFiKCJNb250aCIpICsgeWxhYigiVG90YWwgTmV3IE1lbWJlcnMiKSArIGdlb21fY29sKCkrIAogICAgICAgICAgICBnZW9tX3RleHQoYWVzKGxhYmVsPW5ld19tZW1iZXJzKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdD0gMC41KSwKICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIiwgc2l6ZSA9IDUpICsgY29vcmRfZmxpcCgpICsgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSByZXYobGV2ZWxzKGFnZ19qb2lucyRtb250aCkpKQphbGxfam9pbnMgPSBhbGxfam9pbnMgKyBsYWJzKHRpdGxlID0gIk5ldyBNZW1iZXIgSm9pbnMgQWNyb3NzIHRoZSBZZWFyIikKYWxsX2pvaW5zCmBgYAoKIyMgYWxsIGxlYXZlcwpJIGRlY2lkZWQgdG8gYWxzbyBtYWtlIGEgZ3JhcGggZm9yIGxlYXZlcywgYnV0IGl0IHdhcyB1bHRpbWF0ZWx5IHNjcmFwcGVkIGJlY2F1c2Ugb3VyIGFuYWx5c2lzIHdhcyBtb3JlIGZvY3VzZWQgaW4gdGhlIG5ldyB1c2VyIGNoYW5nZXMuIFBlcmhhcHMgd2UgY2FuIHJldHVybiB0byBhbmFseXplIHRoZSBsZWF2ZXMKYGBge3J9CmxlYXZlcwphZ2dfbGVhdmVzLjIwMTkKYWdnX2xlYXZlcy4yMDIwCmFnZ19sZWF2ZXMuMjAyMQphZ2dfbGVhdmVzCmBgYApgYGB7cn0KYWxsX2xlYXZlcyA9IGdncGxvdChkYXRhID0gYWdnX2xlYXZlcywgbWFwcGluZyA9IGFlcyh4ID0gbW9udGgsIHkgPSBsZWF2ZXJzLCBmaWxsID0geWVhcikpICsgeGxhYigiTW9udGgiKSArIHlsYWIoIlRvdGFsIExlYXZlcyIpICsgZ2VvbV9jb2woKSsgCiAgICAgICAgICAgICBnZW9tX3RleHQoYWVzKGxhYmVsPWxlYXZlcnMpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0PSAwLjUpLAogICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIiwgc2l6ZSA9IDUpICsgY29vcmRfZmxpcCgpICsgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSByZXYobGV2ZWxzKGFnZ19sZWF2ZXMkbW9udGgpKSkKYWxsX2xlYXZlcyA9IGFsbF9sZWF2ZXMgKyBsYWJzKHRpdGxlID0gIk1lbWJlciBMZWF2ZXMgQWNyb3NzIHRoZSBZZWFyIikKCmFsbF9sZWF2ZXMKYGBgCgojIyBhbGwgY29tbXVuaWNhdG9ycwpgYGB7cn0KY29tbXVuaWNhdG9ycwoKYWdnX2NvbW0uMjAxOQphZ2dfY29tbS4yMDIwCmFnZ19jb21tLjIwMjEKYWdnX2NvbW1zCmBgYApgYGB7cn0KYWxsX2NvbW1zID0gZ2dwbG90KGRhdGEgPSBhZ2dfY29tbXMsIG1hcHBpbmcgPSBhZXMoeCA9IG1vbnRoLCB5ID0gdG90YWxfY29tbXVuaWNhdGVkLCBmaWxsID0geWVhcikpICsgeGxhYigiTW9udGgiKSArIHlsYWIoIlRvdGFsIE1lbWJlcnMgQ29tbXVuaWNhdGVkIikgKyAKICAgICAgICAgICAgZ2VvbV9jb2woKSsgZ2VvbV90ZXh0KGFlcyhsYWJlbD10b3RhbF9jb21tdW5pY2F0ZWQpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0PSAwLjUpLAogICAgICAgICAgICBjb2xvdXIgPSAid2hpdGUiLCBzaXplID0gNSkgKyBjb29yZF9mbGlwKCkgKyBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IHJldihsZXZlbHMoYWdnX2NvbW1zJG1vbnRoKSkpCmFsbF9jb21tcyA9IGFsbF9jb21tcyArIGxhYnModGl0bGUgPSAiQWxsIENvbW11bmljYXRpbmcgTWVtYmVycyIpCmFsbF9jb21tcwpgYGAKCiMgbGluZWFyIG1vZGVscwpUaGlzIHNlY3Rpb24gY29udGFpbnMgdGhlIGNvZGUgZm9yIGdlbmVyYXRpbmcgbGluZWFyIG1vZGVscyBmb3IgdGhlIG90aGVyIHZhcmlhYmxlcyB3ZSB3ZXJlIGludGVyZXN0ZWQgaW4uIEkgZm9sbG93ZWQgbXkgcHJvZmVzc29yJ3Mgbm90ZXMgZm9yIHNldHRpbmcgdXAgdGhlIHBhcmFtZXRlcnMuIEZvciBmdW4gSSBkZWNpZGVkIHRvIGV4cGVyaW1lbnQgd2l0aCB0aGUgbWVzc2FnZXMgZGF0YXNldCwgYXMgaXQgaW5jbHVkZWQgYW4gYWRkaXRpb25hbCB2YXJpYWJsZSBvZiBgbWVzc2FnZXNfcGVyX2NvbW11bmljYXRvcmAgd2hpY2ggZ2l2ZXMgYSBiaXQgbW9yZSBncmFudWxhcml0eSBpbiBjb21wYXJpbmcgYmV0d2VlbiBpbmRpdmlkdWFscyBhbmQgYWdncmVnYXRlcyBmb3IgbWVzc2FnZXMuCgojIyBuZXcgbWVtYmVycyBsaW5lYXIgbW9kZWwKYGBge3J9CmpvaW5zCmpvaW5zX2xtID0gbG0obmV3X21lbWJlcnMgfiBtb250aCArIHllYXJfdHlwZSwgZGF0YSA9IGpvaW5zKQpwcmludChzdW1tYXJ5KGpvaW5zX2xtKSkKYGBgCgojIyB0b3RhbCBtZXNzYWdlcyBsaW5lYXIgbW9kZWwKYGBge3J9Cm1lc3NhZ2VzCm1lc3NhZ2VzX2xtID0gbG0obWVzc2FnZXMgfiBtb250aCArIHllYXJfdHlwZSwgZGF0YSA9IG1lc3NhZ2VzKQpwcmludChzdW1tYXJ5KG1lc3NhZ2VzX2xtKSkKYGBgCiMjIG1lc3NhZ2VzIGV4cGVyaW1lbnRzCiMjIyBpbmNsdWRpbmcgbWVzc2FnZXNfcGVyX2NvbW11bmljYXRvciBpbiBmdWxsIG1vZGVsCmBgYHtyfQptZXNzYWdlcwptZXNzYWdlc19sbTEgPSBsbShtZXNzYWdlcyB+IG1vbnRoICsgeWVhcl90eXBlICsgbWVzc2FnZXNfcGVyX2NvbW11bmljYXRvciwgZGF0YSA9IG1lc3NhZ2VzKQpwcmludChzdW1tYXJ5KG1lc3NhZ2VzX2xtMSkpCmBgYAojIyMgaW5jbHVkaW5nIG1lc3NhZ2VzX3Blcl9jb21tdW5pY2F0b3IgaW4gZnVsbCBtb2RlbApgYGB7cn0KbWVzc2FnZXMKbWVzc2FnZXNfbG0yID0gbG0obWVzc2FnZXNfcGVyX2NvbW11bmljYXRvciB+IG1vbnRoICsgeWVhcl90eXBlLCBkYXRhID0gbWVzc2FnZXMpCnByaW50KHN1bW1hcnkobWVzc2FnZXNfbG0yKSkKYGBgCgojIyB2b2ljZXMgbGluZWFyIG1vZGVsCmBgYHtyfQp2b2ljZXMKdm9pY2VzX2xtID0gbG0oc3BlYWtpbmdfbWludXRlcyB+IG1vbnRoICsgeWVhcl90eXBlLCBkYXRhID0gdm9pY2VzKQpwcmludChzdW1tYXJ5KHZvaWNlc19sbSkpCmBgYAoKIyMgY29tbXVuaWNhdG9ycyBsaW5lYXIgbW9kZWwKYGBge3J9CmNvbW11bmljYXRvcnMKY29tbXVuaWNhdG9yc19sbSA9IGxtKHRvdGFsX2NvbW11bmljYXRlZCB+IG1vbnRoICsgeWVhcl90eXBlLCBkYXRhID0gY29tbXVuaWNhdG9ycykKcHJpbnQoc3VtbWFyeShjb21tdW5pY2F0b3JzX2xtKSkKYGBgCgoKIyBtZXNzaW5nIHdpdGggdG9wIHZhbHVlcwpgYGB7cn0KIyBkYXRhZnJhbWVfbmFtZVt3aXRoKGRhdGFmcmFtZV9uYW1lLCBvcmRlcihjb2x1bW5fbmFtZSkpLCBdCmRmPXZvaWNlW3dpdGgodm9pY2Usb3JkZXIoImNvbW11bmljYXRvcnMiKSksXQpkZgpgYGAK